package com.shengcheng.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import com.alibaba.fastjson.JSONObject;
import com.shengcheng.AlbumFeignClient;
import com.shengcheng.CategoryFeignClient;
import com.shengcheng.UserFeignClient;
import com.shengcheng.constant.RedisConstant;
import com.shengcheng.entity.*;
import com.shengcheng.query.AlbumIndexQuery;
import com.shengcheng.repository.AlbumRepository;
import com.shengcheng.repository.SuggestRepository;
import com.shengcheng.result.RetVal;
import com.shengcheng.service.SearchService;
import com.shengcheng.util.PinYinUtils;
import com.shengcheng.vo.AlbumInfoIndexVo;
import com.shengcheng.vo.AlbumSearchResponseVo;
import com.shengcheng.vo.AlbumStatVo;
import com.shengcheng.vo.UserInfoVo;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.parsers.ReturnTypeParser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private AlbumRepository albumRepository;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserFeignClient userFeignClient;
    @Qualifier("genericReturnTypeParser")
    @Autowired
    private ReturnTypeParser genericReturnTypeParser;

    @Autowired
    private SuggestRepository suggestRepository;

    @Override
    public void onSaleAlbum(Long albumId) {
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        AlbumInfo albumInfo = albumFeignClient.getAlbumInfoById(albumId).getData();
        BeanUtils.copyProperties(albumInfo, albumInfoIndex);
        //根据id查询专辑属性值
        List<AlbumAttributeValue> albumPropertyValueList = albumFeignClient.getAlbumPropertyValue(albumId);

        if (!CollectionUtils.isEmpty(albumPropertyValueList)) {
            List<AttributeValueIndex> valueIndicesList = albumPropertyValueList.stream().map(albumPropertyValue -> {
                        AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                        BeanUtils.copyProperties(albumPropertyValue, attributeValueIndex);
                        return attributeValueIndex;
                    }
            ).collect(Collectors.toList());
            albumInfoIndex.setAttributeValueIndexList(valueIndicesList);

        }
        Long category3Id = albumInfo.getCategory3Id();
        BaseCategoryView categoryVive = categoryFeignClient.getCategoryVive(category3Id);
        albumInfoIndex.setCategory1Id(categoryVive.getCategory1Id());
        albumInfoIndex.setCategory2Id(categoryVive.getCategory2Id());

        //根据用户id查询用户信息
        UserInfoVo UserInfo = userFeignClient.getUserById(albumInfo.getUserId()).getData();
        albumInfoIndex.setAnnouncerName(UserInfo.getNickname());
        //还需要查询统计表 、、不写了
        albumRepository.save(albumInfoIndex);
        //专辑自动补全信息放入
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        suggestIndex.setTitle(albumInfo.getAlbumTitle());
        suggestIndex.setKeyword(new Completion(new String[]{albumInfo.getAlbumTitle()}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfo.getAlbumTitle())}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfo.getAlbumTitle())}));
        suggestRepository.save(suggestIndex);
        //专辑主播名称自动补全
        if (!StringUtils.isEmpty(albumInfoIndex.getAnnouncerName())) {
            SuggestIndex announcerSuggestIndex = new SuggestIndex();
            announcerSuggestIndex.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            announcerSuggestIndex.setTitle(albumInfoIndex.getAnnouncerName());
            announcerSuggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAnnouncerName()}));
            announcerSuggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAnnouncerName())}));
            announcerSuggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAnnouncerName())}));
            suggestRepository.save(announcerSuggestIndex);
        }
    }

    @Override
    public void offSaleAlbum(Long albumId) {
        albumRepository.deleteById(albumId);
    }

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Override
    public List<Map<Object, Object>> getChannelData(Long category1Id) throws IOException {
        //1.获取三级分类信息
        List<BaseCategory3> category3List = categoryFeignClient.getCategory3ListByCategory1Id(category1Id).getData();
        //2.把三级分类id转换为List<FieldValue>
        List<FieldValue> category3FieldValueList = category3List.stream().map(BaseCategory3::getId)
                //FieldValue.of的作用是把id包装成_kind和_value类型
                .map(x -> FieldValue.of(x)).collect(Collectors.toList());
        //3.搜索ES语句 传递一个searchRequest
        SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(s -> s
                        .index("albuminfo")
                        .query(q -> q
                                .terms(t -> t
                                        .field("category3Id")
                                        .terms(new TermsQueryField.Builder().value(category3FieldValueList).build())
                                )
                        )
                        .aggregations("category3IdAgg", a -> a.terms(t -> t.field("category3Id"))
                                .aggregations("topSixHotScoreAgg", xa -> xa.topHits(t -> t.size(6)
                                        .sort(xs -> xs.field(f -> f.field("hotScore").order(SortOrder.Desc)))))),
                AlbumInfoIndex.class);
        //4.建立三级分类id和三级分类对象的映射
        Map<Long, BaseCategory3> category3Map = category3List.stream().collect(Collectors
                .toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));
        //4.解析数据放到对象当中
        Aggregate category3IdAgg = response.aggregations().get("category3IdAgg");
        List<Map<Object, Object>> topAlbumInfoIndexMapList = category3IdAgg.lterms().buckets().array().stream().map(bucket -> {
            Long category3Id = bucket.key();
            Aggregate topSixHotScoreAgg = bucket.aggregations().get("topSixHotScoreAgg");
            List<AlbumInfoIndex> topAlbumInfoIndexList = topSixHotScoreAgg.topHits().hits().hits().stream().map(hit ->
                    JSONObject.parseObject(hit.source().toString(), AlbumInfoIndex.class)
            ).collect(Collectors.toList());
            Map<Object, Object> retMap = new HashMap<>();
            retMap.put("baseCategory3", category3Map.get(category3Id));
            retMap.put("list", topAlbumInfoIndexList);
            return retMap;
        }).collect(Collectors.toList());
        return topAlbumInfoIndexMapList;
    }

    @Override
    @SneakyThrows
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        //生成DSL语句
        //对dls语句调用
        //对结果解析
        //设置其他参数
        //设置总页数
        SearchRequest request = BuildQueryDsl(albumIndexQuery);
        SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(request, AlbumInfoIndex.class);
        //解析
        AlbumSearchResponseVo responseVo = parseSearchResponse(response);
        responseVo.setPageNo(albumIndexQuery.getPageNo());
        responseVo.setPageSize(albumIndexQuery.getPageSize());
        //设置总页数
        responseVo.setTotalPages((responseVo.getTotal() + responseVo.getPageSize() - 1) / responseVo.getPageSize());
        return responseVo;
    }

    @Override
    @SneakyThrows
    public HashSet<String> autoCompleteSuggest(String keyword) {
        Suggester suggester = new Suggester.Builder()
                .suggesters("suggestionKeyword", s -> s
                        .prefix(keyword)
                        .completion(c -> c.field("keyword")))
                .suggesters("suggestionKeywordPinyin", s -> s
                        .prefix(keyword)
                        .completion(c -> c.field("keywordPinyin")))
                .suggesters("suggestionKeywordSequence", s -> s
                        .prefix(keyword)
                        .completion(c -> c.field("keywordSequence"))).build();
        System.out.println(suggester.toString());
        SearchResponse<SuggestIndex> suggestResponse = elasticsearchClient.search(s -> s
                .index("suggestinfo")
                .suggest(suggester), SuggestIndex.class);
        //2.解析自动补全结果
        HashSet<String> suggestSet = analysisResponse(suggestResponse);
        //3.如果上面自动补全的比较少
        if (suggestSet.size() < 5) {
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(s -> s.index("suggestinfo")
                            .size(10)
                            .query(q -> q.match(m -> m.field("title").query(keyword)))
                    , SuggestIndex.class);
            List<Hit<SuggestIndex>> suggestHitList = searchResponse.hits().hits();
            for (Hit<SuggestIndex> suggestHit : suggestHitList) {
                suggestSet.add(suggestHit.source().getTitle());
                int size = suggestSet.size();
                //自动补全不要超过10个
                if (size > 10) break;
            }
        }
        return suggestSet;

    }

//    public Map<String, Object> getAlbumDetail(Long albumId) {
//        Map<String, Object> retMap = new HashMap<>();
//        System.out.println(Thread.currentThread().getName());
//        //这里我们先不对异常进行处理
//        CompletableFuture<AlbumInfo> albumFuture = CompletableFuture.supplyAsync(() -> {
//            //a.专辑基本信息 已写
//            AlbumInfo albumInfo = albumFeignClient.getAlbumInfoById(albumId).getData();
//            retMap.put("albumInfo", albumInfo);
//            System.out.println(Thread.currentThread().getName());
//            return albumInfo;
//        },myPoolExecutor);
//        CompletableFuture<Void> statFuture = CompletableFuture.runAsync(() -> {
//            //b.专辑统计信息 未写
//            AlbumStatVo albumStatInfo = albumFeignClient.getAlbumStatInfo(albumId);
//            retMap.put("albumStatVo", albumStatInfo);
//            System.out.println(Thread.currentThread().getName());
//        },myPoolExecutor);
//        CompletableFuture<Void> categoryViewFuture = albumFuture.thenAcceptAsync(albumInfo -> {
//            //c.专辑分类信息 已写
//            BaseCategoryView categoryView = categoryFeignClient.getCategoryView(albumInfo.getCategory3Id());
//            retMap.put("baseCategoryView", categoryView);
//            System.out.println(Thread.currentThread().getName());
//        },myPoolExecutor);
//        CompletableFuture<Void> announcerFuture = albumFuture.thenAcceptAsync(albumInfo -> {
//            //d.用户基本信息 已写
//            UserInfoVo userInfoVo = userFeignClient.getUserById(albumInfo.getUserId()).getData();
//            retMap.put("announcer", userInfoVo);
//            System.out.println(Thread.currentThread().getName());
//        },myPoolExecutor);
//        CompletableFuture.allOf(albumFuture, statFuture, categoryViewFuture, announcerFuture).join();
//        return retMap;
//    }
    @Override
    public Map<String, Object> getAlbumDetail(Long albumId) {
        Map<String, Object> retMap = new HashMap<>();
        //a.专辑基本信息 已写
        AlbumInfo albumInfo = albumFeignClient.getAlbumInfoById(albumId).getData();
        retMap.put("albumInfo", albumInfo);
        //b.专辑统计信息 未写
        AlbumStatVo albumStatInfo = albumFeignClient.getAlbumStatInfo(albumId);
        retMap.put("albumStatVo", albumStatInfo);
        //c.专辑分类信息 已写
        BaseCategoryView categoryView = categoryFeignClient.getCategoryVive(albumInfo.getCategory3Id());
        retMap.put("baseCategoryView", categoryView);
        //d.用户基本信息 已写
        UserInfoVo userInfoVo = userFeignClient.getUserById(albumInfo.getUserId()).getData();
        retMap.put("announcer", userInfoVo);
        return retMap;


    }

    @Autowired
    private RedisTemplate redisTemplate;
    @SneakyThrows
    @Override
    public void updateRanking() {
        //排行榜，按分类维度统计
        List<BaseCategory1> category1List = categoryFeignClient.getCategory1().getData();
        if (!CollectionUtils.isEmpty(category1List)) {
            for (BaseCategory1 baseCategory1 : category1List) {
                //统计维度：热度:hotScore、播放量:playStatNum、订阅量:subscribeStatNum、购买量:buyStatNum、评论数:albumCommentStatNum
                String[] rankingTypeList = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
                for (String rankingType : rankingTypeList) {
                    SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(s -> s
                            .index("albuminfo")
                            .size(10)
                            .sort(t -> t.field(f -> f.field(rankingType).order(SortOrder.Desc))), AlbumInfoIndex.class);

                    //解析查询列表
                    List<AlbumInfoIndex> albumList = new ArrayList<>();
                    response.hits().hits().stream().forEach(hit -> {
                        AlbumInfoIndex album = hit.source();
                        albumList.add(album);
                    });
                    //将排行榜数据记录redis
                    redisTemplate.boundHashOps(RedisConstant.RANKING_KEY_PREFIX + baseCategory1.getId()).put(rankingType, albumList);
                }
            }
        }
    }

    private HashSet<String> analysisResponse(SearchResponse<SuggestIndex> suggestResponse) {
        HashSet<String> suggestSet = new HashSet<>();
        Map<String, List<Suggestion<SuggestIndex>>> suggestMap = suggestResponse.suggest();
        suggestMap.entrySet().stream().forEach(suggestEntry -> {
            List<Suggestion<SuggestIndex>> suggestValueList = suggestEntry.getValue();
            suggestValueList.stream().forEach(suggestValue -> {
                List<String> suggestTitleList = suggestValue.completion().options().stream()
                        .map(m -> m.source().getTitle()).collect(Collectors.toList());
                suggestSet.addAll(suggestTitleList);
            });
        });
        return suggestSet;
    }

    private AlbumSearchResponseVo parseSearchResponse(SearchResponse<AlbumInfoIndex> response) {
        AlbumSearchResponseVo responseVo = new AlbumSearchResponseVo();
        //获取总记录
        responseVo.setTotal(response.hits().total().value());
        //获取专辑列表信息
        List<Hit<AlbumInfoIndex>> searchHits = response.hits().hits();
        List<AlbumInfoIndexVo> albumInfoIndexVos = new ArrayList<>();
        for (Hit<AlbumInfoIndex> searchHit : searchHits) {
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(searchHit.source(), albumInfoIndexVo);
            //设置高亮
            List<String> albumTitle = searchHit.highlight().get("albumTitle");
            if(albumTitle != null){
                albumInfoIndexVo.setAlbumTitle(albumTitle.get(0));
            }
            albumInfoIndexVos.add(albumInfoIndexVo);
        }

        responseVo.setList(albumInfoIndexVos);
        return responseVo;
    }

    private SearchRequest BuildQueryDsl(AlbumIndexQuery albumIndexQuery) {
        //构造bool
        //构造最外层的query
        BoolQuery.Builder boolQuery = new BoolQuery.Builder();
        String keyword = albumIndexQuery.getKeyword();
        if (!StringUtils.isNotBlank(keyword)) {
            //构造should关键字查询
          boolQuery.should(s -> s.match(m -> m.field("albumTitle").query(keyword)));
          boolQuery.should(s -> s.match(m -> m.field("albumIntro").query(keyword)));
          boolQuery.should(s -> s.match(m -> m.field("announcerName").query(keyword)));

        }
        //根据一级分类查询 二级分类 三级分类
        Long category1Id = albumIndexQuery.getCategory1Id();
        if (category1Id != null) {
            boolQuery.filter(f -> f.term(t -> t.field("category1Id").value(category1Id)));
        }
        Long category2Id = albumIndexQuery.getCategory2Id();
        if (category2Id != null) {
            boolQuery.filter(f -> f.term(t -> t.field("category2Id").value(category2Id)));
        }
        Long category3Id = albumIndexQuery.getCategory3Id();
        if (category3Id != null) {
            boolQuery.filter(f -> f.term(t -> t.field("category3Id").value(category3Id)));
        }

        //嵌套查询  过滤 根据分类
        List<String> propertyList = albumIndexQuery.getAttributeList();
        if(!CollectionUtils.isEmpty(propertyList)){
            for (String property : propertyList) {
                String[] propertySplit = property.split(":");
                if(propertySplit != null && propertySplit.length == 2){
                    boolQuery.filter(f -> f.
                            nested(n -> n.
                                    path("attributeValueIndexList").
                                    query(q -> q.bool(b -> b
                            .must(m -> m.term(t -> t.field("attributeValueIndexList.attributeId").value(propertySplit[0])))
                            .must(m -> m.term(t -> t.field("attributeValueIndexList.valueId").value(propertySplit[1])))))));
                }
            }
        }

        //最外层query
        Query query = boolQuery.build()._toQuery();
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        //构造分页和高亮
        SearchRequest.Builder searchRequest = new SearchRequest.Builder()
                .index("albuminfo")
                .query(query)
                .from((pageNo - 1) * pageSize)
                .size(pageSize)
                .highlight(h -> h.fields("albumTitle", f -> f.preTags("<font color='red'>").postTags("</font>")));
        //构造排序
        String order = albumIndexQuery.getOrder();
        String[] orderSplit = order.split(":");
        String orderField = "";
        if(!StringUtils.isEmpty(order)){
            if(orderSplit != null && orderSplit.length == 2){
                switch (orderSplit[0]){
                    case "1":
                        orderField = "hotScore";
                        break;
                    case "2":
                        orderField = "playStatNum";
                        break;
                    case "3":
                        orderField = "createTime";
                        break;
                }
            }
            String sortType = orderSplit[1];
            String finalOrderField = orderField;
            searchRequest.sort(s -> s.field(f -> f.field(finalOrderField)
                    .order("asc".equals(sortType)?SortOrder.Asc:SortOrder.Desc)));
        }
       SearchRequest request = searchRequest.build();
        return request;

    }



}
